/**
 * @author Fei Chen
 * Latest update: 2008/1/17
 * Display torch relay schedule and routes on Google maps.
 * Written by Mingjin Wu first, continued by Fei Chen
 */

/**
 * A customs class.
 * Relative to the map in the browser.
 */
function TMap(gmap_, Torch, region_, additional_) {
  /**
   * The private property.
   */
  var grayIcon = createIcon(Torch.URL_ICON_G, new GSize(12, 12),
                            new GPoint(6, 6), new GPoint(6, 4));
      
  /**
   * The private property.
   */
  var redIcon = createIcon(Torch.URL_ICON_R, new GSize(12, 12),
                           new GPoint(6, 6), new GPoint(6, 4));
  
  /**
   * The private property.
   */
  var tIcon = createIcon(Torch.URL_ICON_T, new GSize(24, 24),
                         new GPoint(4, 20), new GPoint(4, 4));
                         
  /**
   * The center coordinate of the world.
   */
  var worldCenter = new GLatLng(37.633300, 21.616759);

  /**
   * The center cordinate of China.
   */
  var chinaCenter = new GLatLng(34.264915, 108.954401);                       

  /**
   * The private property.
   */
  var gmap = gmap_;
  
  var olMgr = new OverlayManager(gmap, Torch, region_, additional_);
  
  /**
   * The private property.
   */
  var tMarker = null;
  
  var region = region_;
  
  var bubbleListener = null;
  
  GEvent.addListener(gmap, "zoomend", function(oldL, newL) {
    if(olMgr.zoomer(oldL, newL)) {
      if (tMarker) {
        gmap.removeOverlay(tMarker);
      }
      gmap.closeInfoWindow();
    }
  });
  
  /**
   * Private function of the class.
   * Create GIcon object.
   */
  function createIcon(image, iconSize, iconAnchor, infoWindowAnchor) {
    var icon = new GIcon();
    icon.image = image;
    icon.iconSize = iconSize;
    icon.iconAnchor = iconAnchor;
    icon.infoWindowAnchor = infoWindowAnchor;
    return icon;
  }
  
  /**
   * Private function of the class.
   * Add marker event listener.
   */
  function addMarkerEvent(marker, city) {
    GEvent.addListener(marker, 'click', function() {
      changeCity(city);
      Torch.setCity(city, Torch.EVENT_M);
    });
  }

  this.setRegion = function(reg) {
    
    if(reg != region)
      return;
    var l = gmap.getZoom();
    switch (region) {
      case Torch.REGION_WD :
        if(l < Torch.LEVEL_WD[0])
          gmap.setCenter(worldCenter, Torch.LEVEL_WD_DF);
        break;
      case Torch.REGION_CD :
        if(l < Torch.LEVEL_CN[0])
          gmap.setCenter(chinaCenter,Torch.LEVEL_CN_DF);
        break;
    }
  }
  
  this.reset = function() {
    olMgr.refresh(gmap.getZoom());
  }
  
  this.init = function(){
    if (Torch.tCity) {
      var p = new GLatLng(Torch.tCity.la, Torch.tCity.ln);
      tMarker = new GMarker(p, { icon : tIcon });
      gmap.addOverlay(tMarker);
      gmap.panTo(p);
      addMarkerEvent(tMarker, Torch.tCity);
    } 
  }
  
  /**
   * Public function of the class.
   * Change the current city in the map.
   * @param {City} city The target city to go to.
   */
  this.setCity = function(city) {
    var l = gmap.getZoom();
    switch (city.type) {
      case Torch.TYPE_WDCT :
        if(l < Torch.LEVEL_WD[0] || l > Torch.LEVEL_WD[1])
          gmap.setZoom(Torch.LEVEL_WD_DF);
        break;
      case Torch.TYPE_CNPV :
        if(l < Torch.LEVEL_CN_PV[0] || l > Torch.LEVEL_CN_PV[1])
          gmap.setZoom(Torch.LEVEL_CN_DF);
        break;
      case Torch.TYPE_CNCT :
        if(l < Torch.LEVEL_CN_CT[0] || l > Torch.LEVEL_CN_CT[1])
          gmap.setZoom(Torch.LEVEL_PV_DF);
        break;
      case Torch.TYPE_MTCT :
        if(l < 13 )
          gmap.setZoom(13);
        break;
    }
    changeCity(city);
  }

  /**
   * Public function of the class.
   * Define relative maker and polyline. 
   * NOTE: There is still not add city into map.
   * 
   */
  this.importCity = function(list, sIdx, eIdx, status, type) {
    var icon = (status == Torch.STATUS_N) ? grayIcon : redIcon;
    var points = [];
    if (sIdx != 0 && list[sIdx - 1]) {
      points.push(new GLatLng(list[sIdx - 1].la, list[sIdx - 1].ln));
    }
    for (var i = sIdx, ct, pt, mk; i <= eIdx; i++) {
      ct = list[i];
      pt = new GLatLng(ct.la, ct.ln);
      mk = new GMarker(pt, { icon : icon, title : ct.n });
      olMgr.addMarker(mk, type);
      addMarkerEvent(mk, ct);
      points.push(pt);
    }
    if (type != Torch.TYPE_CNPV && type != Torch.TYPE_MTCT && points.length > 0) {
      var color = (status == Torch.STATUS_N) ? Torch.COLOR_G : Torch.COLOR_R;
      for (var i = 1, point, line; point = points[i]; i++) {
        line = new GPolyline([points[i - 1], point], color, 2, 1);
        olMgr.addPolyline(line, type);
      }
    }
  }
  
  /**
   * The private function of the class.
   */
  function changeCity(city) {
    var p = new GLatLng(city.la, city.ln);
    if (tMarker) {
      gmap.removeOverlay(tMarker);
      tMarker.setPoint(p);
    } else {
      tMarker = new GMarker(p, { icon : tIcon });
    }
    gmap.addOverlay(tMarker);
    addMarkerEvent(tMarker, city);
    openInfoWindow(tMarker, city);
    panToVisible(p);
  }

  function openInfoWindow(marker, city) {
    
    var tabs = [];
    if (city.type == Torch.TYPE_CNPV) {  // Province
      tabs.push(new GInfoWindowTab(city.n, getProvHtml(city)));
    } else {
      var index = city.id.match(/\D+(\d+)/)[1];
      var idx = ( city.type == Torch.TYPE_WDCT ) ? index : (parseInt(index) + 23);
      idx = ( city.type == Torch.TYPE_MTCT ) ? (parseInt(idx) + 113) : idx;
      var info, video;
      if(Torch.infoList){
        info = Torch.infoList[idx];
      } else {
        GDownloadUrl(Torch.URL_D + Torch.NAME_INFO, function(data){
          Torch.infoList = eval(data);
          openInfoWindow(marker, city);
        });
        return;
      }
      if(Torch.videoList){
        video = Torch.videoList[idx];
      } else {
        GDownloadUrl(Torch.URL_D + Torch.NAME_VIDEO, function(data){
          Torch.videoList = eval(data);
          openInfoWindow(marker, city);
        });
        return;
      }
      var infoHtml = getInfoHtml(city, info, idx);
      var videoHtml = getVideoHtml(city, video, info, idx);
      var imgHtml = getImgHtml(city, info, idx);
      tabs.push(new GInfoWindowTab(Torch.tabT_p, imgHtml));
      tabs.push(new GInfoWindowTab(Torch.tabT_i, infoHtml));
      tabs.push(new GInfoWindowTab(Torch.tabT_v, videoHtml));
    }
    marker.openInfoWindowTabsHtml(tabs, { disableGoogleLinks : true, maxWidth : 320 , selectedTab : 0});
  }
  
  function panToVisible(point){
    var x=0, y=0;
    var pxl = gmap.fromLatLngToContainerPixel(point);
    if(pxl.x > 500 || pxl.y > 800){
      gmap.setCenter(point);
      pxl = gmap.fromLatLngToContainerPixel(point);
    }
    if(pxl.x < 150) {
      x = 150 - pxl.x;
    } else if(pxl.x > 350){
       x = 350 - pxl.x;
    }
    if(pxl.y < 400){
      y = 400 - pxl.y;
    }else if(pxl.y > 500){
      y = 500 - pxl.y;
    }
    gmap.panBy(new GSize(x, y));
  }
  
  function getProvHtml(city) {
    var html = '<div style="font-size: 12pt; font-weight: bold;">'
               + city.n + '</div>';
    html = '<div style="font-size: 12pt; font-weight: bold;">' + city.n;
    html += '<span style="font-size: 10pt; color: #C0C0C0; margin-left:15px;">'
            + Torch.toLocaleDateCN(city.d) + '</span></div>';
    html += '<div style="margin:5px;font-size:13px;">' + Torch.passL + '</div>';
    html += '<div style="margin:5px; border-top:solid 2px #C0C0C0">';
    for(var i = 0; i < city.c.length ; i++){
      var child = Torch.cnctList[city.c[i]];
      html += '<p style="position:relative; font-size:13px; color: #000;margin:5px;clear:both;">';
      html += child.n + '<span style="position:absolute; right:5px; color: #C0C0C0;">';
      html += Torch.toLocaleDateCN(child.d)+'</span></p>';
    }
    html += '</div>';
    return html;
  }
  
  function getVideoHtml(city, vdo, info, idx) {
    var id = (vdo.videoID.search(/\*/) != -1) ? '1005_2004_3000_4000_2008_03_26_15_45_39_9554' : vdo.videoID;
    var title = (vdo.videoTitle.search(/\*/) != -1) ? Torch.vT : vdo.videoTitle;
    var html = '<div class="video">';
    html += '<p class="sub-head"><span class="city-name">' + city.n + '</span><p class="video-title">' + title + '</p></p>'; 
    if(id.match(/^\d{4}_\d{4}/)){
      html += '<object height="242" width="300" style="z-index:200;">';
      html += ' <param name="movie" value="http://sports.cctv.com/playcfg/player.swf"></param>';
      html += ' <param name="allowScriptAccess" value="always"></param>';
      html += ' <param name="AutoStart" value="0"></param>';
      html += ' <param name="wmode" value="window"></param>';
      html += ' <param name="flashvars" value="size=1&amp;id=' + id + '.xml&amp;site=http://sports.cctv.com"></param>';
      html += ' <embed flashvars="size=1&amp;id=' + id + '.xml&amp;site=http://sports.cctv.com" ';
      html += '       src="http://sports.cctv.com/playcfg/player.swf" ';
      html += '       type="application/x-shockwave-flash" ';
      html += '       allowfullscreen="true" ';
      html += '       wmode="window" ';
      html += '       autostart="false"';
      html += '       allowscriptaccess="always" height="242" width="300"></embed>';
      html += '</object>';
    }else{
      html += '<object height="242" width="300" style="z-index:200;">';
      html += ' <param name="movie" value="http://sports.cctv.com/playcfg/player_new.swf"></param>';
      html += ' <param name="allowScriptAccess" value="always"></param>';
      html += ' <param name="AutoStart" value="0"></param>';
      html += ' <param name="wmode" value="window"></param>';
      html += ' <param name="flashvars" value="id=' + id + '&amp;site=http://sports.cctv.com&method=http"></param>';
      html += ' <embed flashvars="id=' + id + '&amp;site=http://sports.cctv.com&method=http" ';
      html += '       src="http://sports.cctv.com/playcfg/player_new.swf" ';
      html += '       type="application/x-shockwave-flash" ';
      html += '       allowfullscreen="true" ';
      html += '       wmode="window" ';
      html += '       autostart="false"';
      html += '       allowscriptaccess="always" height="242" width="300"></embed>';
      html += '</object>';
    }
    html += '<p class="info-copy">' + Torch.vcpL + '</p>';
    html += '<p class="info-link" >';
    html += ' <span class="link-head" >' + Torch.linkL + '</span>';
    html += ' <div class="link-content">';
    html += '   <p><a href="' + Torch.videoU + Torch.pa + encodeURIComponent(info.name) + '" target="_blank">' + Torch.moreL + info.name + Torch.videoL + '</a></p>';
    if(city.type != Torch.TYPE_MTCT){
      html += '  <p>' + Torch.tvN + '<a href="' + Torch.vU + idx + '" target="_blank">' + info.name + Torch.vL + '</a></p>';
    }
    html += ' </div>';
    html += '</p></div>';
    return html;
  }
  
  function getImgHtml(city, info, idx) {
    if(info.picU.search(/@(\d*)@/) != -1){
      info.picU = info.picU.replace(/@(\d*)@/, Torch.pr[info.picU.match(/@(\d*)@/)[1]-1]);
    }
    var html = '<div class="pic">';
    html += ' <p class="sub-head"><span class="city-name">' + city.n + '</span>' + info.picT + '</p>';
    html += ' <p>';
    html += '  <a href="' + info.picU + '" target="_blank"><img src="' + info.picU + '"/></a>';
    html += '  <p class="info-copy">' + Torch.pcpL + '</p>';
    html += ' </p>';
    html += '<p class="info-link" >';
    html += ' <span class="link-head" >' + Torch.linkL + '</span>';
    html += ' <div class="link-content">';
    html += '   <p><a href="' + Torch.imgU + Torch.pa + encodeURIComponent(info.name) + '" target="_blank">' + Torch.moreL + info.name + Torch.imgL + '</a></p>';
    if(city.type != Torch.TYPE_MTCT){
      html += '   <p>' + Torch.tvN + '<a href="' + Torch.aU + idx + '" target="_blank">' + info.name + Torch.aL + '</a></p>';
    }
    html += ' </div>';
    html += '</p></div>';
    return html;
  }
  
  /**
   * The private function of the class.
   */
  function getInfoHtml(city, info, idx) {
    var html = '<div class="intro">';
    var subTitle = (city.type == Torch.TYPE_MTCT) ? city.d : Torch.toLocaleDateCN(city.d);
    html += '<p class="sub-head"><span class="city-name">' + city.n + '</span>' + subTitle + '</p>';
    html += '<p class="intro-detail">' + info.intro + '  </p>';
    if(info.next != ""){
      html += '<p class="intro-next"><span>' + Torch.nextL + '</span>' + info.next + '</p>';
    }
    html += '<p class="info-link" >';
    html += ' <span class="link-head" >' + Torch.linkL + '</span>';
    html += ' <div class="link-content">';
    html += '  <p><a href="' + Torch.pgU + Torch.pa + encodeURIComponent(info.name) + '" target="_blank">' + Torch.moreL + info.name + Torch.pgL + '</a></p>';
    if(city.type == Torch.TYPE_CNCT){
      html += ' <p><a href="' + Torch.lU + encodeURIComponent(info.name) + '" target="_blank">' + info.name + Torch.lL + '</a></p>';
    }
    if(city.type != Torch.TYPE_MTCT){
      html += '  <p><a href="' + Torch.tvU + '" target="_blank">' + Torch.tvL + '</a></p>';
      html += '  <p><a href="' + Torch.rU + idx+'" target="_blank">' + Torch.rL + '</a></p>';
    }
    html += ' </div>';
    html += '</p></div>';
    return html;
  }
  
  this.showSpecial = function(){
    gmap.setCenter(new GLatLng(27.989058229073375, 86.92464351654053), 11);
    gmap.setMapType(G_PHYSICAL_MAP);
    olMgr.showSpecial();
  }
  
  this.hideSpecial = function(){
    gmap.setCenter(new GLatLng(34.264915, 108.954401), Torch.LEVEL_CN_DF);
    gmap.setMapType(G_NORMAL_MAP);
    olMgr.hideSpecial();
  }
}

/**
 * To manage the markers and polylines.
 * @param {GMap2} gmap
 */
function OverlayManager(gmap, Torch, region_, additional_) {
  /**
   * The private property.
   */
  var map = gmap;
  
  var region = region_;
  
  /**
   * The private property.
   * All overlays arrays' first elem store the flag as added or not
   * and the second elem store the flag as showed or not.
   */
  var wdctMarkers = [false/*hasAdded*/, false/*hasShowed*/];
  
  /**
   * The private property.
   */
  var cnctMarkers = [false, false];
  var mtctMarkers = [false, false];
  
  /**
   * The private property.
   */
  var cnpvMarkers = [false, false];
  
  /**
   * The private property.
   */
  var wdLines = [false, false];
  
  /**
   * The private property.
   */
  var cnLines = [false, false];
  var mtLines = [false, false];
  if(additional_) mtLines.push(additional_);

  /**
   * Show all the overlays in the given list.
   * If the overlays do not added to the map, add them.
   * @param {Array} overlayList The list of overlays.
   */
  function showOverlays(overlayList) {
    if (!overlayList[0]) {
      add(overlayList);
      overlayList[0] = true;
      overlayList[1] = true;
    } else if (!overlayList[1]) {
      show(overlayList);
      overlayList[1] = true;
    }

    /**
     * Private function of showOverlays(), add the overlays into map.
     * @param {Array} overlayList
     */
    function add(overlayList) {
      for (var i = 2, overlay; overlay = overlayList[i]; i++) {
        map.addOverlay(overlay);
      }
    }

    /**
     * Private function of showOverlays(), show the overlays.
     * @param {Array} overlayList 
     */
    function show(overlayList) {
      for (var i = 2, overlay; overlay = overlayList[i]; i++) {
        overlay.show();
      }
    }
  }

  /**
   * Private function of the class.
   * Hide all the overlays in the given list, if still not added to the map,
   * do nothing.
   * @param {Array} overlayList
   */
  function hideOverlays(overlayList) {
    if (overlayList[0] && overlayList[1]) {
      hide(overlayList);
      overlayList[1] = false;
    }

    /**
     * Private function of hideOverlays(), hide the overlays.
     * @param {Array} overlayList 
     */
    function hide(overlayList) {
      for (var i = 2, overlay; overlay = overlayList[i]; i++) {
        overlay.hide();
      }
    }
  }

  /**
   * Public function of the class.
   * Add marker into the overlayManager.
   * @param {GMarker} marker
   * @param {int} cityType
   */
  this.addMarker = function(marker, cityType) {
    switch (cityType) {
      case Torch.TYPE_WDCT :
        wdctMarkers.push(marker);
        break;
      case Torch.TYPE_CNCT :
        cnctMarkers.push(marker);
        break;
      case Torch.TYPE_CNPV :
        cnpvMarkers.push(marker);
        break;
      case Torch.TYPE_MTCT :
        mtctMarkers.push(marker);
    }
  }

  /**
   * Public function of the class.
   * Add polyline into the overlayManager.
   * @param {GPolyline} polyline
   * @param {int} cityType
   */
  this.addPolyline = function(polyline, cityType) {
    switch (cityType) {
      case Torch.TYPE_WDCT :
        wdLines.push(polyline);
        break;
      case Torch.TYPE_CNCT :
        cnLines.push(polyline);
    }
  }

  /**
   * Public function of the class.
   * Invoked when the region is changed.
   * @param {int} region
   */
  this.refresh = function(level) {
//    map.clearOverlays();  //TODO need hide all first
    
    var r = getLevelRange(level);
    showMarkers(r);
    showPolylines(r);
  }

  /**
   * Private function of the class.
   * Show special markers according to the level range.
   * @param {int} levelRange
   */
  function showMarkers(levelRange) {
    switch (levelRange) {
      case Torch.RANGE_CN_PV :
        showOverlays(cnpvMarkers);
        break;
      case Torch.RANGE_CN_CT :
        showOverlays(cnctMarkers);
        break;
      case Torch.RANGE_WD :
        showOverlays(wdctMarkers);
    }
  }

  /**
   * Private function of the class.
   * Hide special markers according to the level range.
   * @param {int} levelRange
   */
  function hideMarkers(levelRange) {
    switch (levelRange) {
      case Torch.RANGE_CN_PV :
        hideOverlays(cnpvMarkers);
        break;
      case Torch.RANGE_CN_CT :
        hideOverlays(cnctMarkers);
        break;
      case Torch.RANGE_WD :
        hideOverlays(wdctMarkers);
    }
  }

  /**
   * Private function of the class.
   * Show special polylines according to the level range.
   * @param {int} levelRange
   */
  function showPolylines(levelRange) {
    switch (levelRange) {
      case Torch.RANGE_WD :
        showOverlays(wdLines);
        break;
      case Torch.RANGE_CN_PV :
      case Torch.RANGE_CN_CT :
        showOverlays(cnLines);
        showOverlays(mtLines);
    }
  }

  /**
   * Private function of the class.
   * Hide special polylines according to the level range.
   * @param {Object} levelRange
   */
  function hidePolylines(levelRange) {
    switch (levelRange) {
      case Torch.RANGE_WD :
        hideOverlays(wdLines);
        break;
      case Torch.RANGE_CN_PV :
      case Torch.RANGE_CN_CT :
        hideOverlays(cnLines);
        hideOverlays(mtLines);
    }
  }

  function isLevel(lel, range){
    return (range[0] <= lel && lel <= range[1]);
  }
  
  /**
   * Private function of the class.
   * Get level range according to the given level.
   * @param {int} level The Google map's level.
   */
  function getLevelRange(level) {
    if (region == Torch.REGION_WD) {
      return isLevel(level, Torch.LEVEL_WD) ? Torch.RANGE_WD : null;
    } else {
      if(isLevel(level, Torch.LEVEL_CN_PV)){
        return Torch.RANGE_CN_PV;
      } else if (isLevel(level, Torch.LEVEL_CN_CT)) {
        return Torch.RANGE_CN_CT;
      } else {
        return null;
      }
    }
    return null;
  }

  /**
   * Public function of the class.
   * Control the overlays' display on the map according to the levels. 
   * @param {int} oldLevel The Google Map's level. 
   *                       Need to hide overlays on this level.
   * @param {int} newLevel The Google Map's level.
   *                       Need to show overlays on this level.
   */
  this.zoomer = function(oldLevel, newLevel) {
    var oldLevelRange = getLevelRange(oldLevel);
    var newLevelRange = getLevelRange(newLevel);
    if (oldLevelRange != newLevelRange) {
      hideMarkers(oldLevelRange);
      hidePolylines(oldLevelRange);
      showMarkers(newLevelRange);
      showPolylines(newLevelRange);
      return true;
    }
    return false;
  }
  
  this.hideSpecial = function(){
    hideOverlays(mtctMarkers);
  }
  this.showSpecial = function(){
    showOverlays(mtctMarkers);
  }
}
